/*
 * Copyright (c) 2023-2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "drawing_font.h"

#include "src/utils/SkUTF.h"

#include "drawing_canvas_utils.h"
#include "text/font.h"

using namespace Keels;
using namespace Rosen;
using namespace Drawing;

static Font* CastToFont(Keels_Drawing_Font* cFont)
{
    return reinterpret_cast<Font*>(cFont);
}

static const Font* CastToFont(const Keels_Drawing_Font* cFont)
{
    return reinterpret_cast<const Font*>(cFont);
}

static const Font& CastToFont(const Keels_Drawing_Font& cFont)
{
    return reinterpret_cast<const Font&>(cFont);
}

static Typeface* CastToTypeface(Keels_Drawing_Typeface* cTypeface)
{
    return reinterpret_cast<Typeface*>(cTypeface);
}

void Keels_Drawing_FontSetEdging(Keels_Drawing_Font* cFont, Keels_Drawing_FontEdging cEdging)
{
    Font* font = CastToFont(cFont);
    if (font == nullptr) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_INVALID_PARAMETER;
        return;
    }
    if (cEdging < KEELS_FONT_EDGING_ALIAS || cEdging > KEELS_FONT_EDGING_SUBPIXEL_ANTI_ALIAS) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_PARAMETER_OUT_OF_RANGE;
        return;
    }
    font->SetEdging(static_cast<FontEdging>(cEdging));
}

Keels_Drawing_FontEdging Keels_Drawing_FontGetEdging(const Keels_Drawing_Font* cFont)
{
    const Font* font = CastToFont(cFont);
    if (font == nullptr) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_INVALID_PARAMETER;
        return KEELS_FONT_EDGING_ALIAS;
    }
    return static_cast<Keels_Drawing_FontEdging>(font->GetEdging());
}

void Keels_Drawing_FontSetHinting(Keels_Drawing_Font* cFont, Keels_Drawing_FontHinting cHinting)
{
    Font* font = CastToFont(cFont);
    if (font == nullptr) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_INVALID_PARAMETER;
        return;
    }
    if (cHinting < KEELS_FONT_HINTING_NONE || cHinting > KEELS_FONT_HINTING_FULL) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_PARAMETER_OUT_OF_RANGE;
        return;
    }
    font->SetHinting(static_cast<FontHinting>(cHinting));
}

Keels_Drawing_FontHinting Keels_Drawing_FontGetHinting(const Keels_Drawing_Font* cFont)
{
    const Font* font = CastToFont(cFont);
    if (font == nullptr) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_INVALID_PARAMETER;
        return KEELS_FONT_HINTING_NONE;
    }
    return static_cast<Keels_Drawing_FontHinting>(font->GetHinting());
}

void Keels_Drawing_FontSetForceAutoHinting(Keels_Drawing_Font* cFont, bool isForceAutoHinting)
{
    Font* font = CastToFont(cFont);
    if (font == nullptr) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_INVALID_PARAMETER;
        return;
    }
    font->SetForceAutoHinting(isForceAutoHinting);
}

bool Keels_Drawing_FontIsForceAutoHinting(const Keels_Drawing_Font* cFont)
{
    const Font* font = CastToFont(cFont);
    if (font == nullptr) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_INVALID_PARAMETER;
        return false;
    }
    return font->IsForceAutoHinting();
}

void Keels_Drawing_FontSetBaselineSnap(Keels_Drawing_Font* cFont, bool baselineSnap)
{
    Font* font = CastToFont(cFont);
    if (font == nullptr) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_INVALID_PARAMETER;
        return;
    }
    font->SetBaselineSnap(baselineSnap);
}

bool Keels_Drawing_FontIsBaselineSnap(const Keels_Drawing_Font* cFont)
{
    const Font* font = CastToFont(cFont);
    if (font == nullptr) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_INVALID_PARAMETER;
        return false;
    }
    return font->IsBaselineSnap();
}

void Keels_Drawing_FontSetSubpixel(Keels_Drawing_Font* cFont, bool isSubpixel)
{
    Font* font = CastToFont(cFont);
    if (font == nullptr) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_INVALID_PARAMETER;
        return;
    }
    font->SetSubpixel(isSubpixel);
}

bool Keels_Drawing_FontIsSubpixel(const Keels_Drawing_Font* cFont)
{
    const Font* font = CastToFont(cFont);
    if (font == nullptr) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_INVALID_PARAMETER;
        return false;
    }
    return font->IsSubpixel();
}

Keels_Drawing_Font* Keels_Drawing_FontCreate()
{
    Font* font = new Font();
    font->SetTypeface(g_LoadZhCnTypeface());
    return (Keels_Drawing_Font*)font;
}

void Keels_Drawing_FontSetTypeface(Keels_Drawing_Font* cFont, Keels_Drawing_Typeface* cTypeface)
{
    Font* font = CastToFont(cFont);
    if (font == nullptr) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_INVALID_PARAMETER;
        return;
    }
    font->SetTypeface(std::shared_ptr<Typeface> { CastToTypeface(cTypeface), [](auto p) {} });
}

Keels_Drawing_Typeface* Keels_Drawing_FontGetTypeface(Keels_Drawing_Font* cFont)
{
    Font* font = CastToFont(cFont);
    if (font == nullptr) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_INVALID_PARAMETER;
        return nullptr;
    }
    return (Keels_Drawing_Typeface*)(font->GetTypeface().get());
}

void Keels_Drawing_FontSetTextSize(Keels_Drawing_Font* cFont, float textSize)
{
    Font* font = CastToFont(cFont);
    if (font == nullptr) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_INVALID_PARAMETER;
        return;
    }
    font->SetSize(textSize);
}

float Keels_Drawing_FontGetTextSize(const Keels_Drawing_Font* cFont)
{
    const Font* font = CastToFont(cFont);
    if (font == nullptr) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_INVALID_PARAMETER;
        return -1.0f;
    }
    return font->GetSize();
}

int Keels_Drawing_FontCountText(
    Keels_Drawing_Font* cFont, const void* text, size_t byteLength, Keels_Drawing_TextEncoding encoding)
{
    if (cFont == nullptr || text == nullptr) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_INVALID_PARAMETER;
        return 0;
    }
    Font* font = CastToFont(cFont);
    return font->CountText(text, byteLength, static_cast<TextEncoding>(encoding));
}

uint32_t Keels_Drawing_FontTextToGlyphs(const Keels_Drawing_Font* cFont, const void* text, uint32_t byteLength,
    Keels_Drawing_TextEncoding encoding, uint16_t* glyphs, int maxGlyphCount)
{
    if (cFont == nullptr || text == nullptr || glyphs == nullptr || byteLength == 0 || maxGlyphCount <= 0) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_INVALID_PARAMETER;
        return 0;
    }
    return CastToFont(*cFont).TextToGlyphs(
        text, byteLength, static_cast<TextEncoding>(encoding), glyphs, maxGlyphCount);
}

void Keels_Drawing_FontGetWidths(const Keels_Drawing_Font* cFont, const uint16_t* glyphs, int count, float* widths)
{
    if (cFont == nullptr || glyphs == nullptr || widths == nullptr || count <= 0) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_INVALID_PARAMETER;
        return;
    }
    CastToFont(*cFont).GetWidths(glyphs, count, widths);
}

Keels_Drawing_ErrorCode Keels_Drawing_FontMeasureSingleCharacter(
    const Keels_Drawing_Font* cFont, const char* str, float* textWidth)
{
    if (cFont == nullptr || str == nullptr || textWidth == nullptr) {
        return KEELS_DRAWING_ERROR_INVALID_PARAMETER;
    }
    size_t len = strlen(str);
    if (len == 0) {
        return KEELS_DRAWING_ERROR_INVALID_PARAMETER;
    }
    const char* currentStr = str;
    int32_t unicode = SkUTF::NextUTF8(&currentStr, currentStr + len);
    *textWidth = CastToFont(*cFont).MeasureSingleCharacter(unicode);
    return KEELS_DRAWING_SUCCESS;
}

Keels_Drawing_ErrorCode Keels_Drawing_FontMeasureText(const Keels_Drawing_Font* cFont, const void* text, size_t byteLength,
    Keels_Drawing_TextEncoding encoding, Keels_Drawing_Rect* bounds, float* textWidth)
{
    if (cFont == nullptr || text == nullptr || byteLength == 0 || textWidth == nullptr) {
        return KEELS_DRAWING_ERROR_INVALID_PARAMETER;
    }

    *textWidth = CastToFont(*cFont).MeasureText(
        text, byteLength, static_cast<TextEncoding>(encoding), reinterpret_cast<Drawing::Rect*>(bounds));
    return KEELS_DRAWING_SUCCESS;
}

void Keels_Drawing_FontSetLinearText(Keels_Drawing_Font* cFont, bool isLinearText)
{
    Font* font = CastToFont(cFont);
    if (font == nullptr) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_INVALID_PARAMETER;
        return;
    }
    font->SetLinearMetrics(isLinearText);
}

bool Keels_Drawing_FontIsLinearText(const Keels_Drawing_Font* cFont)
{
    const Font* font = CastToFont(cFont);
    if (font == nullptr) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_INVALID_PARAMETER;
        return false;
    }
    return font->IsLinearMetrics();
}

void Keels_Drawing_FontSetTextSkewX(Keels_Drawing_Font* cFont, float skewX)
{
    Font* font = CastToFont(cFont);
    if (font == nullptr) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_INVALID_PARAMETER;
        return;
    }
    font->SetSkewX(skewX);
}

float Keels_Drawing_FontGetTextSkewX(const Keels_Drawing_Font* cFont)
{
    const Font* font = CastToFont(cFont);
    if (font == nullptr) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_INVALID_PARAMETER;
        return -1.0f;
    }
    return font->GetSkewX();
}

void Keels_Drawing_FontSetFakeBoldText(Keels_Drawing_Font* cFont, bool isFakeBoldText)
{
    Font* font = CastToFont(cFont);
    if (font == nullptr) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_INVALID_PARAMETER;
        return;
    }
    font->SetEmbolden(isFakeBoldText);
}

bool Keels_Drawing_FontIsFakeBoldText(const Keels_Drawing_Font* cFont)
{
    const Font* font = CastToFont(cFont);
    if (font == nullptr) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_INVALID_PARAMETER;
        return false;
    }
    return font->IsEmbolden();
}

void Keels_Drawing_FontSetScaleX(Keels_Drawing_Font* cFont, float scaleX)
{
    Font* font = CastToFont(cFont);
    if (font == nullptr) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_INVALID_PARAMETER;
        return;
    }
    font->SetScaleX(scaleX);
}

float Keels_Drawing_FontGetScaleX(const Keels_Drawing_Font* cFont)
{
    const Font* font = CastToFont(cFont);
    if (font == nullptr) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_INVALID_PARAMETER;
        return -1.0f;
    }
    return font->GetScaleX();
}

void Keels_Drawing_FontSetEmbeddedBitmaps(Keels_Drawing_Font* cFont, bool isEmbeddedBitmaps)
{
    Font* font = CastToFont(cFont);
    if (font == nullptr) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_INVALID_PARAMETER;
        return;
    }
    font->SetEmbeddedBitmaps(isEmbeddedBitmaps);
}

bool Keels_Drawing_FontIsEmbeddedBitmaps(const Keels_Drawing_Font* cFont)
{
    const Font* font = CastToFont(cFont);
    if (font == nullptr) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_INVALID_PARAMETER;
        return false;
    }
    return font->IsEmbeddedBitmaps();
}

void Keels_Drawing_FontDestroy(Keels_Drawing_Font* cFont)
{
    if (!cFont) {
        return;
    }
    delete CastToFont(cFont);
}

float Keels_Drawing_FontGetMetrics(Keels_Drawing_Font* cFont, Keels_Drawing_Font_Metrics* cFontMetrics)
{
    float ret = -1;
    Font* font = CastToFont(cFont);
    if (font == nullptr || cFontMetrics == nullptr) {
        g_drawingErrorCode = KEELS_DRAWING_ERROR_INVALID_PARAMETER;
        return ret;
    }
    FontMetrics metrics;
    ret = font->GetMetrics(&metrics);

    cFontMetrics->top = metrics.fTop;
    cFontMetrics->ascent = metrics.fAscent;
    cFontMetrics->descent = metrics.fDescent;
    cFontMetrics->leading = metrics.fLeading;
    cFontMetrics->bottom = metrics.fBottom;
    return ret;
}